home *** CD-ROM | disk | FTP | other *** search
- {*********************************************************}
- {* AA6Pack *}
- {* Copyright (c) Julian M Bucknall 1999 *}
- {* All rights reserved. *}
- {*********************************************************}
- {* SixBitPack compression and decompression *}
- {*********************************************************}
-
- {Note: this unit is released as freeware. In other words, you are free
- to use this unit in your own applications, however I retain all
- copyright to the code. JMB}
-
- unit AA6Pack;
-
- interface
-
- uses
- SysUtils, Classes;
-
- {$IFOPT D+}
- {$DEFINE InDebugMode}
- {$ENDIF}
-
- procedure SixBitPackCompress(aInStream, aOutStream : TStream);
- procedure SixBitPackDecompress(aInStream, aOutStream : TStream);
-
- implementation
-
- const
- AcceptedChars : string[63] =
- 'abcdefghijklmnopqrstuvwxyz' + {26}
- 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + {52}
- ' .,;:-()!'^M^J; {63}
- const
- Escape = $00;
- EndOfData = byte('Z');
-
- {===Helper routines==================================================}
- function ReadBits(aBitCount : integer;
- aStream : TStream;
- var aCollByte : byte;
- var aCollCount: integer) : byte;
- const
- Masks : array [0..7] of byte =
- ($01, $02, $04, $08, $10, $20, $40, $80);
- var
- i : integer;
- TempCollByte : byte;
- TempCollCount : integer;
- BytesRead : longint;
- begin
- {make temporary copies of the var parameters for speed}
- TempCollByte := aCollByte;
- TempCollCount := aCollCount;
- {clear resulting byte}
- Result := 0;
- {for all bits...}
- for i := 0 to pred(aBitCount) do begin
- {if we've run out of bits, get another byte from the stream}
- if (TempCollCount = 0) then begin
- BytesRead := aStream.Read(TempCollByte, 1);
- if (BytesRead <> 1) then
- raise Exception.Create('Input stream is exhausted, expecting more data');
- TempCollCount := 8;
- end;
- {shift result to accept next bit (sets low bit to zero)}
- Result := Result shl 1;
- {get topmost bit from collector byte}
- if ((TempCollByte and $80) <> 0) then
- Result := Result or 1;
- {we've used another bit}
- TempCollByte := TempCollByte shl 1;
- dec(TempCollCount);
- end;
- {set new values of var parameters}
- aCollByte := TempCollByte;
- aCollCount := TempCollCount;
- end;
- {--------}
- procedure WriteBits(aValue : byte;
- aBitCount : integer;
- aStream : TStream;
- var aCollByte : byte;
- var aCollCount: integer);
- const
- Masks : array [0..7] of byte =
- ($01, $02, $04, $08, $10, $20, $40, $80);
- var
- MaskInx : integer;
- i : integer;
- TempCollByte : byte;
- TempCollCount : integer;
- begin
- {make temporary copies of the var parameters for speed}
- TempCollByte := aCollByte;
- TempCollCount := aCollCount;
- {start off with the correct mask}
- MaskInx := pred(aBitCount);
- {for all bits...}
- for i := 0 to pred(aBitCount) do begin
- {shift collector byte left by one (sets low bit to zero)}
- TempCollByte := TempCollByte shl 1;
- {if the current bit is set, set low bit of the collector byte}
- if (aValue and Masks[MaskInx]) <> 0 then
- TempCollByte := TempCollByte or 1;
- {we've added one more bit}
- inc(TempCollCount);
- {if the collector byte is full, write it out, reset bit count}
- if (TempCollCount = 8) then begin
- aStream.Write(TempCollByte, 1);
- TempCollCount := 0;
- end;
- {get next mask}
- dec(MaskInx);
- end;
- {set new values of var parameters}
- aCollByte := TempCollByte;
- aCollCount := TempCollCount;
- end;
- {====================================================================}
-
-
- {===Interfaced routines==============================================}
- procedure SixBitPackCompress(aInStream, aOutStream : TStream);
- var
- CollectorByte : byte;
- BitCount : integer;
- BytesRead : longint;
- Encoding : byte;
- Ch : byte;
- begin
- {we've collected no bits so far}
- BitCount := 0;
- {get the first character from the input stream}
- BytesRead := aInStream.Read(Ch, 1);
- {repeat until we run out of characters in the input stream}
- while (BytesRead > 0) do begin
- {get the possible encoding for this character}
- Encoding := Pos(char(Ch), AcceptedChars);
- {write it out (note: we assume that Escape is 0 here)}
- WriteBits(Encoding, 6, aOutStream, CollectorByte, BitCount);
- {if the encoding is zero, the character wasn't found, so output
- the actual character}
- if (Encoding = 0) then
- WriteBits(Ch, 8, aOutStream, CollectorByte, BitCount);
- {get the next byte}
- BytesRead := aInStream.Read(Ch, 1)
- end;
- {output the end-of-data marker}
- WriteBits(Escape, 6, aOutStream, CollectorByte, BitCount);
- WriteBits(EndOfData, 8, aOutStream, CollectorByte, BitCount);
- {if we've some bits left over write them out as well}
- if (BitCount <> 0) then begin
- {shift the bits to the top of the byte}
- CollectorByte := CollectorByte shl (8 - BitCount);
- aOutStream.Write(CollectorByte, 1);
- end;
- end;
- {--------}
- procedure SixBitPackDecompress(aInStream, aOutStream : TStream);
- var
- CollectorByte : byte;
- BitCount : integer;
- EncodedChar : byte;
- Finished : boolean;
- Ch : byte;
- begin
- {we've got no bits to decompress yet}
- BitCount := 0;
- {repeat until we hit the end-of-data marker}
- Finished := false;
- while not Finished do begin
- {get the next encoded character}
- EncodedChar := ReadBits(6, aInStream, CollectorByte, BitCount);
- {check for the escape character}
- if (EncodedChar <> Escape) then
- {..normal character}
- aOutStream.Write(AcceptedChars[EncodedChar], 1)
- else begin
- {..escaped character}
- Ch := ReadBits(8, aInStream, CollectorByte, BitCount);
- if (Ch = EndOfData) then
- Finished := true
- else
- aOutStream.Write(Ch, 1)
- end;
- end;
- end;
- {====================================================================}
-
- end.
-